/**
 *  renderer.cpp
 *  AwkwardPaint - Engine
 *
 *  Date: 09/08/2013
 *
 *  Language: C++
 *
 *  @author Carl McCaffrey
 *
 */
#include "renderer/renderer.h"
 
#include <gl/GL.h>
#include "renderer/opengl/wglext.h"
#include <SOIL.h>

DEVMODE defaultSettings, fullscreenSettings;
bool fullScreen;

PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
		case WM_DESTROY:
			PostQuitMessage(NULL);
			break;

		case WM_SYSCOMMAND:
			switch(wParam){
				case SC_SCREENSAVE:
				case SC_MONITORPOWER:
					return 0;
			}
			break;
	}

	return DefWindowProc(hWnd, msg, wParam, lParam);
}

unsigned int Renderer::LoadTexture(const char* filename, float* w, float* h)
{
    dbg_output->Print("\tLoading Texture %s...\n", filename);

	int width, height;
    unsigned char* image = SOIL_load_image(filename, &width, &height, 0, SOIL_LOAD_RGBA);
    if(!image){
        dbg_output->Print("\tFailed to load image!\n");
    }

	unsigned int id;

    glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &id);
    glBindTexture(GL_TEXTURE_2D, id);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
    glBindTexture(GL_TEXTURE_2D, 0);
    glDisable(GL_TEXTURE_2D);

	*w = (float)width;
	*h = (float)height;

	SOIL_free_image_data(image);

    return id;
}

void Renderer::Init(HINSTANCE* hinstance, float win_width, float win_height, bool full_screen, bool v_sync)
{
	// Setup Variables
    dbg_output->Print("Initializing Renderer...\n");
	hInstance = hinstance;
    width = win_width;
    height = win_height;
    fullscreen = fullScreen = full_screen;
    vsync = v_sync;

	// Create Win32 Window
	WNDCLASSEX wndClass = { NULL };
	wndClass.cbSize = sizeof(WNDCLASSEX);
	wndClass.lpszClassName = "APWindow";
	wndClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE);
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;
	wndClass.hInstance = *hinstance;
	wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(NULL), "icon.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
	wndClass.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), "icon.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.lpszMenuName = NULL;
	

	if(!RegisterClassEx(&wndClass)){
		dbg_output->Print("\tFailed To Register Window!\n");
		return;
	}

	DWORD wndStyle;
	DWORD wndStyleEx;

	EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &defaultSettings);
	if(fullscreen){
		memset(&fullscreenSettings, NULL, sizeof(DEVMODE));
		fullscreenSettings.dmSize = sizeof(DEVMODE);
		fullscreenSettings.dmPelsWidth = static_cast<DWORD>(win_width);
		fullscreenSettings.dmPelsHeight = static_cast<DWORD>(win_height);
		fullscreenSettings.dmBitsPerPel = 32;
		fullscreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		if(ChangeDisplaySettings(&fullscreenSettings, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL){
			wndStyle = WS_POPUP;
			wndStyleEx = WS_EX_APPWINDOW;
			dbg_output->Print("\tEntered Full Screen Mode!\n");
		}else{
			if(MessageBox(hWnd, "Failed To Enter Full Screen Mode!\nRun In Windowed Mode?", "Warning", MB_ICONWARNING | MB_YESNO) == IDYES){
				wndStyle = WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU;
				wndStyleEx = WS_EX_APPWINDOW;
				fullscreenSettings = defaultSettings;
				fullscreen = fullScreen = false;
				dbg_output->Print("\tEntered Windowed Mode\n");
			}else{
				return;
			}
		}
	}else{
		wndStyle = WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU;
		wndStyleEx = WS_EX_APPWINDOW;
		fullscreenSettings = defaultSettings;
	}

	RECT wndRect = { 0, 0, static_cast<long>(win_width), static_cast<long>(win_height) };
	AdjustWindowRectEx(&wndRect, wndStyle, FALSE, wndStyleEx);

	hWnd = CreateWindowEx(wndStyleEx, "APWindow", "AwkwardPaint - Engine", wndStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, wndRect.right - wndRect.left, wndRect.bottom - wndRect.top, NULL, NULL, *hInstance, NULL);
	if(!hWnd){
		dbg_output->Print("Failed To Create Window!\n");
		return;
	}

	ShowWindow(hWnd, SW_SHOW);
	SetForegroundWindow(hWnd);
	SetFocus(hWnd);
	dbg_output->Print("\tWindow Created!\n");

    dbg_output->Print("\tConfiguring OpenGL...\n");

	// Set Up OpenGL
	PIXELFORMATDESCRIPTOR pfd;
    int iFormat;

    hDC = GetDC(hWnd);

    ZeroMemory(&pfd, sizeof(pfd));
    pfd.nSize = sizeof(pfd);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 24;
    pfd.cDepthBits = 16;
    pfd.iLayerType = PFD_MAIN_PLANE;

    iFormat = ChoosePixelFormat(hDC, &pfd);

    SetPixelFormat(hDC, iFormat, &pfd);

    hRC = wglCreateContext(hDC);
    wglMakeCurrent(hDC, hRC);

	wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");

	if(vsync){
		wglSwapIntervalEXT(1);
	}else{
		wglSwapIntervalEXT(0);
	}


    glClearColor(0.0f, 0.0f, 0.25f, 1.0f);
    glViewport(0, 0, width, height);

    glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0, 1920, 0, 1080, -1, 1);
    glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glDisable(GL_DEPTH_TEST);
        glShadeModel(GL_SMOOTH);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glEnable(GL_ALPHA);
        glEnable(GL_ALPHA_TEST);
        glAlphaFunc(GL_GREATER, 0.1f);
}

void Renderer::Shutdown(void)
{
    dbg_output->Print("Renderer Shutting Down...\n");

	wglMakeCurrent(NULL, NULL);
    wglDeleteContext(hRC);
    ReleaseDC(hWnd, hDC);

    printf("\tDestroying Window!\n");
	ShowCursor(true);
	if(fullscreen){
		ChangeDisplaySettings(&defaultSettings, NULL);
	}
	DestroyWindow(hWnd);
	UnregisterClass("window", *hInstance);
	*hInstance = NULL;
}

void Renderer::BeginFrame(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
}

void Renderer::EndFrame(void)
{
    SwapBuffers(hDC);
}

void Renderer::DrawSprite(Sprite* sprite)
{
    sprite->rect.CalculateVertices();

    glPushMatrix();

    glTranslatef(sprite->rect.pos.x, sprite->rect.pos.y, 0);

    if(sprite->flags & SPRITE_TEXTURED){
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, sprite->tex_id);
        glBegin(GL_TRIANGLE_STRIP);
            glTexCoord2f(0, 0); glVertex2f(sprite->rect.vertices[0].x, sprite->rect.vertices[0].y); // Top Left
            glTexCoord2f(1, 0); glVertex2f(sprite->rect.vertices[1].x, sprite->rect.vertices[1].y); // Top Right
            glTexCoord2f(0, 1); glVertex2f(sprite->rect.vertices[3].x, sprite->rect.vertices[3].y); // Bottom Left
            glTexCoord2f(1, 1); glVertex2f(sprite->rect.vertices[2].x, sprite->rect.vertices[2].y); // Bottom Right
        glEnd();
        glBindTexture(GL_TEXTURE_2D, 0);
        glDisable(GL_TEXTURE_2D);
    }else{
        glColor4f(sprite->r, sprite->g, sprite->b, sprite->a);
        glBegin(GL_TRIANGLE_STRIP);
            glVertex2f(sprite->rect.vertices[0].x, sprite->rect.vertices[0].y);
            glVertex2f(sprite->rect.vertices[1].x, sprite->rect.vertices[1].y);
            glVertex2f(sprite->rect.vertices[3].x, sprite->rect.vertices[3].y);
            glVertex2f(sprite->rect.vertices[2].x, sprite->rect.vertices[2].y);
        glEnd();
        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    }

    glPopMatrix();
}

void Renderer::CreateSprite(Sprite* sprite, unsigned int flags, float x, float y, float w, float h, float scale, float orientation, const char* texture)
{
    dbg_output->Print("Creating Sprite...\n");

    sprite->flags = flags;
    float tex_width, tex_height;

    if((sprite->flags & SPRITE_TEXTURED) && (sprite->flags & SPRITE_SIZE_FROM_IMAGE)){
        sprite->tex_id = LoadTexture(texture, &tex_width, &tex_height);
    }else
        if(sprite->flags & SPRITE_TEXTURED){
            sprite->tex_id = LoadTexture(texture, &tex_width, &tex_height);
        }

    sprite->rect.pos.x = x;
    sprite->rect.pos.y = y;
    if(sprite->flags & SPRITE_SIZE_FROM_IMAGE){
        sprite->rect.size.x = tex_width;
        sprite->rect.size.y = tex_height;
    }else{
        sprite->rect.size.x = w;
        sprite->rect.size.y = h;
    }
    sprite->rect.scale = scale;
    sprite->rect.orientation = orientation;
    sprite->SetColor(1.0f, 0.0f, 0.0f, 1.0f);


    dbg_output->Print("\tPos: %i %i\n", (int)x, (int)y);
    if(sprite->flags & SPRITE_SIZE_FROM_IMAGE){
        dbg_output->Print("\tSize: %i %i\n", (int)tex_width, (int)tex_height);
    }else{
        dbg_output->Print("\tSize: %i %i\n", (int)w, (int)h);
    }
    dbg_output->Print("\tScale: %i\n", (int)scale);
    dbg_output->Print("\tOrientation: %i\n", (int)orientation * (180 / 3.14159));
    if(sprite->flags & SPRITE_TEXTURED){
        dbg_output->Print("\tTextured: true\n\n");
    }else{
        dbg_output->Print("\tTextured: false\n\n");
    }
}

bool Renderer::FullScreen(void)
{
    return fullscreen;
}
